package com.netty.java.nio;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Set;

@Slf4j
public class MultiNioServer implements Runnable {


    public static void main(String[] args) throws Exception {

        MultiNioServer server = new MultiNioServer(8888);

        new Thread(server, "Nio Server").start();

    }


    private Selector selector;

    private ServerSocketChannel serverSocketChannel;

    private volatile boolean stop;

    public MultiNioServer(int port) {

        try {
            serverSocketChannel = ServerSocketChannel.open();
            // 绑定IP PORT
            serverSocketChannel.bind(new InetSocketAddress(port));
            // 非阻塞
            serverSocketChannel.configureBlocking(false);

            selector = Selector.open();
            // 绑定多路复用器
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            log.info("Nio Server started");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public void stop() {
        this.stop = true;
    }

    @Override
    public void run() {

        while (!stop) {

            try {
                // 每秒轮询一次
                if(selector.select(1000) == 0){
                    continue;
                }

                Set<SelectionKey> keys = selector.selectedKeys();
                // 处理 已经就绪的key
                keys.forEach(key -> {
                    handleInput(key);
                });


            } catch (Exception e) {
                e.printStackTrace();
            }

        }

    }

    /***
     * 处理客户端的消息
     * @param key
     */
    public void handleInput(SelectionKey key) {

        // 不合法
        if (!key.isValid()) {
            return;
        }

        // 客户端接入请求
        if (key.isAcceptable()) {

            try {
                ServerSocketChannel channel = (ServerSocketChannel) key.channel();

                SocketChannel sc = channel.accept();

                sc.configureBlocking(false);

                sc.register(selector, SelectionKey.OP_READ);

                log.info("client connected! waiting client sent message");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        if (key.isReadable()) {

            try {
                SocketChannel channel = (SocketChannel) key.channel();

                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

                int count = channel.read(byteBuffer);

                if (count > 0) {

                    byteBuffer.flip();

                    byte[] bytes = new byte[byteBuffer.remaining()];

                    byteBuffer.get(bytes);

                    String message = new String(bytes);

                    log.info("MultiNioServer receive message [{}]", message);

                    // 回复消息
                    doWrite(channel, System.currentTimeMillis());
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        }


    }

    /**
     * 回复消息
     *
     * @param channel
     * @param currentTimeMillis
     */
    private void doWrite(SocketChannel channel, long currentTimeMillis) throws IOException {

        String message = "Server current time is " + currentTimeMillis;

        ByteBuffer buffer = ByteBuffer.allocate(message.getBytes().length);

        buffer.put(message.getBytes());

        channel.write(buffer);
    }


}
